// 09_error_handling.rs
// Rust 错误处理教程

use std::fs::File;
use std::io::{self, Read};

fn main() {
    // 1. panic! 宏用于不可恢复的错误
    // panic!("crash and burn");
    
    // 2. 使用 panic! 的情况
    let v = vec![1, 2, 3];
    // v[99];  // 这会导致 panic!
    
    // 3. Result 枚举用于可恢复的错误
    // Result<T, E> 枚举定义：
    // enum Result<T, E> {
    //     Ok(T),
    //     Err(E),
    // }
    
    // 4. 处理 Result 的一种方式：使用 match
    // let f = File::open("hello.txt");
    
    // let f = match f {
    //     Ok(file) => file,
    //     Err(error) => {
    //         panic!("打开文件时出现问题: {:?}", error)
    //     },
    // };
    
    // 5. 匹配不同的错误情况
    let f = File::open("hello.txt");
    
    let f = match f {
        Ok(file) => file,
        Err(error) => match error.kind() {
            io::ErrorKind::NotFound => match File::create("hello.txt") {
                Ok(fc) => fc,
                Err(error) => panic!("创建文件时出现问题: {:?}", error),
            },
            other_error => {
                panic!("打开文件时出现问题: {:?}", other_error)
            }
        },
    };
    
    // 6. 使用 unwrap 和 expect
    // unwrap 在出错时调用 panic!
    let f = File::open("hello.txt").unwrap();
    
    // expect 允许指定 panic! 时的错误信息
    let f = File::open("hello.txt").expect("无法打开 hello.txt");
    
    // 7. 传播错误
    let username = read_username_from_file();
    match username {
        Ok(name) => println!("用户名: {}", name),
        Err(error) => println!("读取用户名时出错: {}", error),
    }
    
    // 8. 使用 ? 运算符传播错误
    let username = read_username_from_file_short();
    match username {
        Ok(name) => println!("用户名: {}", name),
        Err(error) => println!("读取用户名时出错: {}", error),
    }
    
    // 9. ? 运算符只能用于返回 Result 的函数
    // let f = File::open("hello.txt")?;  // 在 main 函数中不能使用，因为 main 返回 ()
    
    // 10. main 函数返回 Result
    // 为了在 main 函数中使用 ?，可以将 main 函数的返回类型改为 Result
    // 请参见 main_result 函数
}

// 7. 传播错误的函数
fn read_username_from_file() -> Result<String, io::Error> {
    let f = File::open("hello.txt");
    
    let mut f = match f {
        Ok(file) => file,
        Err(e) => return Err(e),
    };
    
    let mut s = String::new();
    
    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}

// 8. 使用 ? 运算符简化错误传播
fn read_username_from_file_short() -> Result<String, io::Error> {
    let mut f = File::open("hello.txt")?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

// 也可以链式调用
fn read_username_from_file_shorter() -> Result<String, io::Error> {
    let mut s = String::new();
    File::open("hello.txt")?.read_to_string(&mut s)?;
    Ok(s)
}

// 10. 返回 Result 的 main 函数
fn main_result() -> Result<(), Box<dyn std::error::Error>> {
    let f = File::open("hello.txt")?;
    
    // 进行其他操作...
    
    Ok(())
}

// 11. 何时使用 panic! 以及何时不使用
// 在示例、原型代码和测试中使用 panic 是合适的
// 在可能预期会失败的操作中应该使用 Result

// 12. 自定义错误类型
#[derive(Debug)]
struct ParseError {
    message: String,
}

impl std::fmt::Display for ParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "解析错误: {}", self.message)
    }
}

impl std::error::Error for ParseError {}

fn parse_number(s: &str) -> Result<i32, ParseError> {
    s.parse::<i32>()
        .map_err(|_| ParseError {
            message: format!("无法解析 '{}' 为数字", s),
        })
}